package com.luo.d3s.ext.component.bus.base;

import com.luo.d3s.ext.component.bus.DomainRegistry;
import org.javatuples.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 根据参数类型调用（分派）对应的服务实现类进行处理
 * <ol>
 *     <li>参数均实现P接口</li>
 *     <li>服务实现类均实现S接口</li>
 * </ol>
 * 注：支持调用多实现方法，不支持返回调用结果
 *
 * @param <S> 服务类型（服务基接口）
 * @param <P> 参数类型（参数基接口）
 * @author luohq
 * @date 2023-01-24 16:38
 */
public class BaseServiceMethodBatchBus<S, P> {

    private static final Logger log = LoggerFactory.getLogger(BaseServiceMethodBatchBus.class);

    /**
     * Map(Command实现类Class, (S实现类实例,Command处理方法))
     */
    private Map<Class<? extends P>, List<Pair<S, Method>>> paramClass2ServiceMethodMap = new HashMap<>();

    /**
     * 参数类型（参数基接口）
     */
    private Class<S> svcBaseInterface;
    /**
     * 服务类型（服务基接口）
     */
    private Class<P> paramBaseInterface;

    /**
     * 构造函数
     *
     * @param svcBaseInterface   服务基接口Class
     * @param paramBaseInterface 待处理参数基接口Class
     */
    public BaseServiceMethodBatchBus(Class<S> svcBaseInterface, Class<P> paramBaseInterface) {
        this.svcBaseInterface = svcBaseInterface;
        this.paramBaseInterface = paramBaseInterface;
    }

    /**
     * 根据参数调用（同步）对应的服务处理方法
     *
     * @param param 命令
     */
    public void dispatch(P param) {
        //待分发参数类型
        Class paramClass = param.getClass();
        //根据待分发参数类型匹配对应的服务处理方法
        List<Pair<S, Method>> svcMethodList = paramClass2ServiceMethodMap.computeIfAbsent(paramClass, paramClassInner -> {
            /** 若对应参数类型的服务处理方法列表缓存不存在，则设置对应参数类型的服务处理方法缓存列表 */
            return (List<Pair<S, Method>>) DomainRegistry.getBeans(this.svcBaseInterface).values().stream()
                    //获取所有public方法
                    .flatMap(svcBean -> Stream.of(AopUtils.getTargetClass(svcBean).getMethods()).map(method -> new Pair(svcBean, method)))
                    //匹配参数类型对应的服务处理方法
                    .filter(svcMethodPair -> matchSvcMethod(svcMethodPair, paramClassInner))
                    .collect(Collectors.<Pair<S, Method>>toList());
        });
        if (CollectionUtils.isEmpty(svcMethodList)) {
            log.error("{} for {} does not exist!", this.svcBaseInterface, paramClass);
            return;
        }
        //调用参数对应的所有处理方法
        svcMethodList.forEach(svcMethodPair -> {
            S svcBeanTarget = svcMethodPair.getValue0();
            Method svcMethod = svcMethodPair.getValue1();
            ReflectionUtils.invokeMethod(svcMethod, svcBeanTarget, param);
        });
    }

    /**
     * 验证服务实现类中的方法是否匹配paramClass（即方法参数类型是否匹配paramClass）
     *
     * @param svcMethodPair (serviceImpl, method)
     * @param paramClass    待处理参数类型
     * @return 是否匹配
     */
    private Boolean matchSvcMethod(Pair<S, Method> svcMethodPair, Class paramClass) {
        try {
            Method method = svcMethodPair.getValue1();
            return (1 == method.getParameterCount() && paramClass.equals(method.getParameterTypes()[0]));
        } catch (Throwable e) {
            return false;
        }
    }
}
